﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;
using System.IO;

namespace RuntimeHelloWorlds
{
	/// <summary>
	/// The classic Reflection.Emit method of generating code at runtime.
	/// Looking at this volume of code it's no wonder DynamicMethod was 
	/// created later on.
	/// </summary>
	public class ReflectionEmitHelloWorld
	{
		public void SayHelloWorld()
		{
			AssemblyName assemblyName = new AssemblyName("ReflectionEmitHelloWorldAssembly");
			AssemblyBuilder assemblyBuilder
				= AppDomain.CurrentDomain
					.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);

			ModuleBuilder moduleBuilder
				= assemblyBuilder.DefineDynamicModule("ReflectionEmitHelloWorldModule");

			TypeBuilder typeBuilder 
				= moduleBuilder
					.DefineType("ReflectionEmitHelloWorldClass", TypeAttributes.Class, typeof(object));

			ConstructorBuilder constructorBuilder = typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);

			MethodBuilder methodBuilder
				= typeBuilder.DefineMethod("HelloWorld", MethodAttributes.Public);

			ILGenerator ilgenerator = methodBuilder.GetILGenerator();

			MethodInfo methodInfo
				= typeof(Console)
					.GetMethod("WriteLine", new[] { typeof(string) });

			ilgenerator.Emit(OpCodes.Ldstr, "Hello, World! (Reflection Emit)");
			ilgenerator.EmitCall(OpCodes.Call, methodInfo, null);
			ilgenerator.Emit(OpCodes.Ret);
			typeBuilder.CreateType();

			assemblyBuilder.Save("1ReflectionEmitHelloWorld.dll");

			Type type = assemblyBuilder.GetTypes().First();
			MethodInfo helloWorldMethod = type.GetMethods().Where(m => m.Name.Equals("HelloWorld")).First();

			Object instance = Activator.CreateInstance(type);
			helloWorldMethod.Invoke(instance, null);
		}
	}
}
